package com.restaurant.application.util.wx;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.nebutown.commonunits.utils.DateTimeUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.spec.AlgorithmParameterSpec;
import java.util.Base64;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

public class WXLogin {

    private static final Logger LOG = LoggerFactory.getLogger(WXLogin.class);

    private final static String NOW_TOKEN = "accessToken";
    private final static Cache<String, String> TOKEN_CACHE = CacheBuilder.newBuilder()
            //设置cache的初始大小为20
            .initialCapacity(20)
            //设置写入并发数为8
            .concurrencyLevel(8)
            //设置cache中的数据存活时间为10秒
            .expireAfterWrite(3600, TimeUnit.SECONDS)
            //构建cache实例
            .build();

    /**
     * 小程序获取access_token
     *
     * @param
     * @return
     */
    public static AccessToken getAccessToken() {
        AccessToken accessToken = new AccessToken();
        if (Objects.nonNull(TOKEN_CACHE.getIfPresent(NOW_TOKEN))) {
            LOG.debug("token from cache at time:{}", DateTimeUtils.getCurrentDateTimeStr());
            accessToken.setAccess_token(TOKEN_CACHE.getIfPresent(NOW_TOKEN));
            return accessToken;
        }
        //拼接获取access_token的请求地址
        String accessTokenUrl = WxConfig.ACCESS_TOKEN_URL.replace("APPID", WxConfig.APPID2).replace("SECRET", WxConfig.SECRET2);
        //发送请求,获取返回结果
        JSONObject jsonObject = HttpsUtils.sendRequest(accessTokenUrl, "GET", null);
        if (jsonObject.getString("access_token") != null) {
            /**
             *  官方返回格式:
             * "access_token":"ACCESS_TOKEN",
             * "expires_in":7200,
             * "errcode":"0",
             * "errmsg":"请求成功"
             */
            accessToken.setAccess_token(jsonObject.getString("access_token"));
            accessToken.setExpires_in(jsonObject.getLong("expires_in"));
            accessToken.setErrcode(jsonObject.getString("errcode"));
            accessToken.setErrmsg(jsonObject.getString("errmsg"));
            TOKEN_CACHE.put(NOW_TOKEN, jsonObject.getString("access_token"));
        } else {
            /**
             * 错误返回样例：
             * "errcode":40013,
             * "errmsg":"invalid code"
             */
            accessToken.setErrcode(jsonObject.getString("errcode"));
            accessToken.setErrmsg(jsonObject.getString("errmsg"));
        }
        return accessToken;
    }

    public static Map<String, String> code2Session(String code) {
        String url = WxConfig.CODE2SESSION_URL.replace("APPID", WxConfig.APPID2)
                .replace("SECRET", WxConfig.SECRET2).replace("JSCODE", code);
        JSONObject jsonObject = HttpsUtils.sendRequest(url, "GET", null);
        Map map = JSON.parseObject(jsonObject.toString(), Map.class);
        return map;
    }

    // 定义微信解密获取手机号码的接口地址，固定的
    static String wxAppHost = "https://api.weixin.qq.com";
    static String wxAppPath = "/sns/jscode2session";

    public static JSONObject decodeWxAppPhone(String encrypted, String iv, String code) {
        String path =
                wxAppPath
                        + "?appid="
                        + WxConfig.APPID2
                        + "&secret="
                        + WxConfig.SECRET2
                        + "&js_code="
                        + code
                        + "&grant_type=authorization_code";
        try {
            // 向微信服务器发送get请求获取加密了的内容
            String sessionkey = code2Session(code).get("session_key");

            // 解密
            byte[] encrypData = Base64.getDecoder().decode(encrypted);
            byte[] ivData = Base64.getDecoder().decode(iv);
            byte[] sessionKey = Base64.getDecoder().decode(sessionkey);
            AlgorithmParameterSpec ivSpec = new IvParameterSpec(ivData);
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            SecretKeySpec keySpec = new SecretKeySpec(sessionKey, "AES");
            cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);

            String resultString = new String(cipher.doFinal(encrypData), StandardCharsets.UTF_8);
            JSONObject object = JSONObject.parseObject(resultString);
            // 拿到手机号码
            String phone = object.getString("phoneNumber");
            // 返回手机号码
            JSONObject returnObject = new JSONObject();
            returnObject.put("phone", phone);
            return returnObject;
        } catch (Exception e) {
            LOG.error("微信小程序手机号码解密异常，信息如下:", e);
        }
        return null;
    }
}
